package ChatSystem;
import java.io.ByteArrayInputStream;
import java.io.ObjectInputStream;
import java.lang.Thread;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.io.IOException;
import java.net.InetAddress;

import chatsystemTDa2.*;

/**
 * This receiver runs in its own thread to receive the ChatSystem internal network messages coming from remote users.
 */
public class UDPReceiver extends Thread {
	private DatagramSocket _socket;
	private ChatNI _cni;
	private volatile boolean _running;
	
	private class MessageAndInfo {
		public Message message;
		public InetAddress address;
		
		public MessageAndInfo(Message m, InetAddress a) {
			message = m;
			address = a;
		}
	}

	/**
	 * Initializes the receiver.
	 * @param cni The Network Interface to notify of the reception of a new message
	 * @throws IOException When the socket could not be bound to the UDP port number
	 */
	UDPReceiver(ChatNI cni) throws IOException {
		_cni = cni;
		_socket = new DatagramSocket(_cni.getPort());
	}
	
	/**
	 * Stops the receiver by closing the socket. Should be the last call to the receiver.
	 */
	public void stopReceiver() {
		_running = false;
		if(_socket != null) {
			_socket.close();
		}
	}
	
	/**
	 * Checks whether the instance is ready to receive messages from the network.
	 * @return The readiness of the instance
	 */
	public boolean running() {
		return _running;
	}
	
	@Override
	public void run() {
		super.run();
				
		MessageAndInfo message;

		_running = true;

		while(_running) {
			message = this.receive();
			if(message != null) {
				_cni.onMessageReceived(message.message, message.address);
			}
			else {
				try {
					Thread.sleep(10);
				}
				catch(Exception e) {}
			}
		}
	}
	
	/**
	 * Blocks until a new message has been received, and returns it associated with the sender Internet address.
	 * @return The pair (message, Internet address) contained in a MessageAndInfo object
	 */
	private MessageAndInfo receive() {
		try {
		    byte[] buf = new byte[1024];
			DatagramPacket recv = new DatagramPacket(buf, buf.length);
			_socket.receive(recv);

			ByteArrayInputStream bytes = new ByteArrayInputStream(buf);
		    ObjectInputStream inputStream = new ObjectInputStream(bytes);
		      
			Message m = (Message)inputStream.readObject();
			if(m != null) {
				return new MessageAndInfo(m, recv.getAddress());
			}
			
			return null;
		
		} catch(Exception e) {
			System.out.println("rcv: " + e.toString());
		}
		return null;
	}

}
